package com.longbig.converter;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.longbig.dto.NodeTypeEnum;
import com.longbig.dto.ParallelGatewayPair;
import com.longbig.dto.X6Edge;
import com.longbig.dto.X6Node;
import com.mysql.cj.xdevapi.JsonArray;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: yyl
 * @date: 2025/4/14:20:40
 */
@Component
public class X6ToBpmnConverter {

    private static String jsonStr = "{\n" +
            "    \"cells\": [\n" +
            "        {\n" +
            "            \"shape\": \"edge\",\n" +
            "            \"id\": \"3c8fbbc2-ac4d-4db5-83fe-6b191d3dbff0\",\n" +
            "            \"zIndex\": 0,\n" +
            "            \"source\": {\n" +
            "                \"cell\": \"a266ffb1-3b7a-48df-8ea2-8255df4d4799\"\n" +
            "            },\n" +
            "            \"target\": {\n" +
            "                \"cell\": \"de75024a-6054-4e8a-bc21-d4f8bb428533\"\n" +
            "            }\n" +
            "        },\n" +
            "        {\n" +
            "            \"shape\": \"edge\",\n" +
            "            \"id\": \"d37667d1-aae4-43b4-99b2-7eaceab5828e\",\n" +
            "            \"zIndex\": 0,\n" +
            "            \"source\": {\n" +
            "                \"cell\": \"de75024a-6054-4e8a-bc21-d4f8bb428533\"\n" +
            "            },\n" +
            "            \"target\": {\n" +
            "                \"cell\": \"b00cd0c0-a465-49df-b7be-ddec290e6e35\"\n" +
            "            }\n" +
            "        },\n" +
            "        {\n" +
            "            \"shape\": \"edge\",\n" +
            "            \"id\": \"6077baed-c1f6-42e5-a073-07b3ab19dcb2\",\n" +
            "            \"zIndex\": 0,\n" +
            "            \"source\": {\n" +
            "                \"cell\": \"de75024a-6054-4e8a-bc21-d4f8bb428533\"\n" +
            "            },\n" +
            "            \"target\": {\n" +
            "                \"cell\": \"693e32e8-e79b-440c-8dea-2327e3a9dea5\"\n" +
            "            }\n" +
            "        },\n" +
            "        {\n" +
            "            \"attrs\": {\n" +
            "                \"text\": {\n" +
            "                    \"text\": \"基础信息\"\n" +
            "                }\n" +
            "            },\n" +
            "            \"shape\": \"custom-main\",\n" +
            "            \"id\": \"a266ffb1-3b7a-48df-8ea2-8255df4d4799\",\n" +
            "            \"zIndex\": 1\n" +
            "        },\n" +
            "        {\n" +
            "            \"attrs\": {\n" +
            "                \"text\": {\n" +
            "                    \"text\": \"割接准备\"\n" +
            "                }\n" +
            "            },\n" +
            "            \"shape\": \"custom-step\",\n" +
            "            \"id\": \"de75024a-6054-4e8a-bc21-d4f8bb428533\",\n" +
            "            \"zIndex\": 2\n" +
            "        },\n" +
            "        {\n" +
            "            \"attrs\": {\n" +
            "                \"text\": {\n" +
            "                    \"text\": \"巡检\"\n" +
            "                }\n" +
            "            },\n" +
            "            \"shape\": \"custom-circle\",\n" +
            "            \"id\": \"b00cd0c0-a465-49df-b7be-ddec290e6e35\",\n" +
            "            \"zIndex\": 4\n" +
            "        },\n" +
            "        {\n" +
            "            \"attrs\": {\n" +
            "                \"text\": {\n" +
            "                    \"text\": \"参数\"\n" +
            "                }\n" +
            "            },\n" +
            "            \"shape\": \"custom-circle\",\n" +
            "            \"id\": \"693e32e8-e79b-440c-8dea-2327e3a9dea5\",\n" +
            "            \"zIndex\": 5\n" +
            "        }\n" +
            "    ]\n" +
            "}";

    private static final ObjectMapper mapper = new ObjectMapper();

    public static void main(String[] args) throws Exception {
        X6ToBpmnConverter converter = new X6ToBpmnConverter();
        String document = converter.buildTopology(jsonStr);
        System.out.println(document);
    }

    // 1. 构建节点拓扑关系
    public String buildTopology(String x6JsonStr) throws Exception {
        JSONObject x6Json = JSONObject.parseObject(x6JsonStr);
        Map<String, X6Node> nodes = new HashMap<>();
        List<X6Edge> edges = new ArrayList<>();
        JSONArray jsonArray = x6Json.getJSONArray("cells");

        //先遍历一遍，建造根节点的map
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject node = jsonArray.getJSONObject(i);
            String nodeId = node.getString("id");
            String shape = node.getString("shape");

            X6Node x6Node = new X6Node();
            x6Node.setId("_" + nodeId.replaceAll("-", ""));
            x6Node.setType(NodeTypeEnum.getNodeTypeEnum(shape));
            if (!"edge".equals(shape)) {
                x6Node.setLabel(node.getJSONObject("attrs").getJSONObject("text")
                        .getString("text"));
            }
            nodes.put(nodeId, x6Node);
        }

        //往下遍历子节点关系
        for (Map.Entry<String, X6Node> x6NodeEntry : nodes.entrySet()) {
            String nodeId = x6NodeEntry.getKey();
            X6Node x6Node = x6NodeEntry.getValue();
            if (x6Node.getType().equals(NodeTypeEnum.STEP)) {
                //遍历shape是edge的节点，找到source匹配nodeID的节点，并添加到children中
                // 遍历cells数组找到所有以当前STEP节点为source的
                for (int i = 0; i < jsonArray.size(); i++) {
                    JSONObject cell = jsonArray.getJSONObject(i);
                    if ("edge".equals(cell.getString("shape"))) {
                        JSONObject source = cell.getJSONObject("source");
                        if (source != null && nodeId.equals(source.getString("cell"))) {
                            // 找到目标节点
                            JSONObject target = cell.getJSONObject("target");
                            String targetNodeId = target.getString("cell");
                            X6Node child = nodes.get(targetNodeId);

                            // 设置父子关系
                            if (child != null) {
                                child.setParentId(nodeId);
                                x6Node.getChildren().add(child);
//                                // 同时建立边关系（可选）
//                                String edgeId = "_" + cell.getString("id").replaceAll("-", "");
//                                X6Edge edge = new X6Edge(edgeId, nodeId, targetNodeId);
//                                x6Node.addOutgoingEdge(edge);
                            }
                        }
                    }
                }
            }
        }


        // 构建边关系
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject node = jsonArray.getJSONObject(i);
            String shape = node.getString("shape");
            if ("edge".equals(shape)) {
                String sourceNodeId = node.getJSONObject("source").getString("cell");
                String sourceId = "_" + sourceNodeId.replaceAll("-", "");
                String targetId = "_" + node.getJSONObject("target").getString("cell").replaceAll("-", "");
                X6Edge edge = new X6Edge("_" + node.getString("id").replaceAll("-", ""), sourceId, targetId);
                nodes.get(sourceNodeId).addOutgoingEdge(edge);
            }
        }

        Document document = generateBpmnXml(nodes, x6JsonStr);
        String xmlStr = documentToString(document);
        return xmlStr;
    }



    // 2. 生成BPMN XML结构 (基于Flowable BPMN模型[7,8](@ref))
    private Document generateBpmnXml(Map<String, X6Node> nodes, String x6JsonStr) throws Exception {
        Document doc = createBpmnDocument();
        Map<String, X6Edge> edges = new HashMap<>();
        Element processElem = doc.createElement("bpmn:process");
        // 增加以下两行配置（关键修复）
        processElem.setAttribute("id", "Process_" + UUID.randomUUID().toString().replaceAll("-", ""));
        //TODO 该名称
        processElem.setAttribute("name", "基础信息");
        processElem.setAttribute("isExecutable", "true");

        // 自动添加起止事件
        String startEventId = "_" + UUID.randomUUID().toString().replaceAll("-", "");
        String endEventId = "_" + UUID.randomUUID().toString().replaceAll("-", "");
        Element startEvent = createStartEvent(doc, startEventId);
        Element endEvent = createEndEvent(doc, endEventId);
        processElem.appendChild(startEvent);
        processElem.appendChild(endEvent);


        // 识别分叉点并插入并行网关
        Map<String, ParallelGatewayPair> gatewayMap = new HashMap<>();
        for (X6Node stepNode : nodes.values()) {
            if (stepNode.getType() != NodeTypeEnum.STEP) {
                continue;
            }
            String stepId = stepNode.getId();
            String nodeId = stepNode.getId().replaceAll("-", "");
            // 为每个步骤创建一对并行网关
            ParallelGatewayPair gateways = createParallelGateways(doc, stepNode.getLabel(), nodeId);
            gatewayMap.put(nodeId, gateways);

            // 将步骤节点替换为网关
            processElem.appendChild(gateways.getForkGateway());
            processElem.appendChild(gateways.getJoinGateway());

            //处理子步骤下的原子能力的边
            List<X6Node> children = stepNode.getChildren();
            for (X6Node child : children) {
                X6Edge edge = new X6Edge();
                edge.setId("flow_" + child.getId().replaceAll("-", ""));
                edge.setSourceId(nodeId);
                edge.setTargetId(child.getId());
                edges.put(edge.getId(), edge);

                //到合并网关的边
                X6Edge edge1 = new X6Edge();
                edge1.setId("flow_" + UUID.randomUUID().toString().replaceAll("-", ""));
                edge1.setSourceId(child.getId());
                edge1.setTargetId(gateways.getJoinGateway().getAttribute("id"));
                edges.put(edge1.getId(), edge1);
            }
        }

//        //网关和任务的边关联
//        nodes.values().stream()
//                .filter(n -> n.getType() == NodeTypeEnum.STEP)
//                .forEach(stepNode -> {
//
//                });

        // 处理服务任务和连接关系
        nodes.values().stream()
                .filter(n -> n.getType() == NodeTypeEnum.SERVICE_TASK)
                .forEach(task -> {
                    Element serviceTask = createServiceTaskElement(task, doc);
                    processElem.appendChild(serviceTask);
                });


//        // 原始错误代码修改为：
//        JSONObject jsonObject = JSONObject.parseObject(x6JsonStr);
//        edges = parseEdgesFromX6(jsonObject); // 从X6数据解析边集合
        edges.values().forEach(edge -> {
            handleEdgeConnections(doc, processElem, edge, gatewayMap);
        });

        // 连接起止网关
        linkStartAndEndEvents(doc, processElem, gatewayMap.values(), startEvent, endEvent);
        doc.getDocumentElement().appendChild(processElem);
        return doc;













//
//
//        // 创建主流程（custom-main）
//        nodes.values().stream()
//                .filter(n -> n.getType().equals(NodeTypeEnum.MAIN_PROCESS))
//                .forEach(mainNode -> {
//                    processElem.setAttribute("id", mainNode.getId().replaceAll("-", ""));
//                    processElem.setAttribute("name", mainNode.getLabel());
//                    processElem.setAttribute("isExecutable", "true");
//
//                    // 处理子流程嵌套（custom-step）
//                    mainNode.getOutgoingEdges().forEach(stepNode -> {
//                        // 为每个步骤创建一对并行网关
//                        ParallelGatewayPair gateways = createParallelGateways(doc, stepNode.getLabel());
//                        gatewayMap.put(stepNode.getId(), gateways);
//
//                        // 将步骤节点替换为网关
//                        process.appendChild(gateways.getForkGateway());
//                        process.appendChild(gateways.getJoinGateway());
//
//
//
//
//                        X6Node subProcess = nodes.get(edge.getTargetId());
//                        Element subElem = createSubProcessElement(subProcess, doc);
//
//                        // 处理原子任务（custom-circle）
//                        subProcess.getOutgoingEdges().forEach(subEdge -> {
//                            X6Node serviceTask = nodes.get(subEdge.getTargetId());
//                            subElem.appendChild(createServiceTaskElement(serviceTask, doc));
//                        });
//
//                        processElem.appendChild(subElem);
//
//                        String edgeId1 = "_" + UUID.randomUUID().toString().replaceAll("-", "");
//                        String edgeId2 = "_" + UUID.randomUUID().toString().replaceAll("-", "");
//
//                        edges.add(new X6Edge(edgeId1, startEventId, subProcess.getId()));
//                        edges.add(new X6Edge(edgeId2, subProcess.getId(), endEventId));
//                    });
//                });
//        // 添加sequenceFlow（基于X6边数据[1](@ref)）
//        edges.forEach(edge ->
//                processElem.appendChild(createSequenceFlow(edge, doc)));
//
//        doc.getDocumentElement().appendChild(processElem);
//        return doc;
    }


    // 创建服务任务（对应serviceTask[6,10](@ref)）
    private Element createServiceTaskElement(X6Node node, Document doc) {
        Element task = doc.createElement("bpmn:serviceTask");
        task.setAttribute("id", node.getId().replaceAll("-", ""));
        task.setAttribute("name", node.getLabel());
        String name = node.getLabel();
        if ("巡检".equals(name)) {
            task.setAttribute("flowable:class",
                    "com.longbig.circle.CircleCheck"); // 委托类配置[10,12](@ref)
        } else if ("参数".equals(name)) {
            task.setAttribute("flowable:class",
                    "com.longbig.circle.CircleParam"); // 委托类配置[10,12](@ref)
        } else {
            //TODO 其他任务

        }

        return task;
    }

    private Document createBpmnDocument() throws ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document doc = dbf.newDocumentBuilder().newDocument();

        // 创建根元素并设置命名空间
        Element definitions = doc.createElementNS(
                "http://www.omg.org/spec/BPMN/20100524/MODEL",
                "bpmn:definitions"
        );
        definitions.setAttribute("xmlns:bpmn", "http://www.omg.org/spec/BPMN/20100524/MODEL");
        definitions.setAttribute("xmlns:flowable", "http://flowable.org/bpmn");
        definitions.setAttribute("targetNamespace", "http://www.example.org/bpmn");
        doc.appendChild(definitions);

        return doc;
    }

    private Element createStartEvent(Document doc, String id) {
        Element startEvent = doc.createElement("bpmn:startEvent");
        startEvent.setAttribute("id", id);
        startEvent.setAttribute("name", "流程开始");
//        // 可扩展表单属性
//        Element extension = doc.createElement("flowable:formProperty");
//        extension.setAttribute("id", "initiator");
//        extension.setAttribute("name", "发起人");
//        startEvent.appendChild(extension);
        return startEvent;
    }

    private Element createEndEvent(Document doc, String id) {
        Element endEvent = doc.createElement("bpmn:endEvent");
        endEvent.setAttribute("id", id);
        endEvent.setAttribute("name", "流程结束");
//        // 终止事件定义（可选）
//        Element termination = doc.createElement("bpmn:terminateEventDefinition");
//        endEvent.appendChild(termination);
        return endEvent;
    }

    private Element createSequenceFlow(X6Edge edge, Document doc) {
        Element sequenceFlow = doc.createElement("bpmn:sequenceFlow");

        // ID处理（Flowable要求ID以字母开头）
        sequenceFlow.setAttribute("id", edge.getId().replaceAll("-", ""));

        // 源和目标引用（添加x6_前缀）
        sequenceFlow.setAttribute("sourceRef", edge.getSourceId());
        sequenceFlow.setAttribute("targetRef", edge.getTargetId());

        // 条件表达式处理（根据X6边的属性）
        if (edge.getConditionExpression() != null) {
            Element conditionExpr = doc.createElement("bpmn:conditionExpression");
            conditionExpr.setAttribute("xsi:type", "bpmn:tFormalExpression");
            conditionExpr.setTextContent(edge.getConditionExpression());
            sequenceFlow.appendChild(conditionExpr);
        }

//        // 扩展属性（如文档标签）
//        Element documentation = doc.createElement("bpmndi:BPMNLabel");
//        documentation.setTextContent("转换自X6边：" + edge.getId());
//        sequenceFlow.appendChild(documentation);

        return sequenceFlow;
    }

    public static JsonNode parseJson(String jsonStr) {
        try {
            // 直接解析为JsonNode树
            return mapper.readTree(jsonStr);
        } catch (Exception e) {
            throw new RuntimeException("JSON解析失败: " + e.getMessage(), e);
        }
    }

    // 在 X6ToBpmnConverter 类中添加以下方法
    public static String documentToString(Document doc) {
        try {
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();

            // 设置输出格式（缩进、编码等）
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

            // 将 Document 转换为字符串
            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(doc), new StreamResult(writer));
            return writer.toString();
        } catch (TransformerException e) {
            throw new RuntimeException("XML转换失败", e);
        }
    }


//    private void handleEdgeConnections(Document doc, Element process,
//                                       X6Node node, Map<String, ParallelGatewayPair> gatewayMap) {
//        String sourceId = edge.getSourceId();
//        String targetId = edge.getTargetId();
//
//        // 原始边转换为BPMN sequenceFlow
//        Element sequenceFlow = doc.createElement("bpmn:sequenceFlow");
//        sequenceFlow.setAttribute("id", "flow_" + edge.getId());
//
//        // 源处理：如果源是步骤节点，替换为分叉网关
//        if (gatewayMap.containsKey(sourceId)) {
//            sequenceFlow.setAttribute("sourceRef",
//                    gatewayMap.get(sourceId).getForkGateway().getAttribute("id"));
//        } else {
//            sequenceFlow.setAttribute("sourceRef", sourceId);
//        }
//
//        // 目标处理：如果目标是步骤节点，替换为合并网关
//        if (gatewayMap.containsKey(targetId)) {
//            sequenceFlow.setAttribute("targetRef",
//                    gatewayMap.get(targetId).getJoinGateway().getAttribute("id"));
//        } else {
//            sequenceFlow.setAttribute("targetRef", targetId);
//        }
//
//        process.appendChild(sequenceFlow);
//    }

    private void linkStartAndEndEvents(Document doc, Element process,
                                       Collection<ParallelGatewayPair> gateways,
                                       Element startEvent, Element endEvent) {
        // 连接开始事件到第一个分叉网关
        if (!gateways.isEmpty()) {
            Element firstFork = gateways.iterator().next().getForkGateway();
            Element startFlow = doc.createElement("bpmn:sequenceFlow");
            startFlow.setAttribute("id", "flow" + startEvent.getAttribute("id").replaceAll("-", ""));
            startFlow.setAttribute("sourceRef", startEvent.getAttribute("id"));
            startFlow.setAttribute("targetRef", firstFork.getAttribute("id"));
            process.appendChild(startFlow);
        }

        // 连接最后一个合并网关到结束事件
        gateways.forEach(pair -> {
            Element flow = doc.createElement("bpmn:sequenceFlow");
            flow.setAttribute("id", "flow" + pair.getJoinGateway().getAttribute("id").replaceAll("-", ""));
            flow.setAttribute("sourceRef", pair.getJoinGateway().getAttribute("id"));
            flow.setAttribute("targetRef", endEvent.getAttribute("id"));
            process.appendChild(flow);
        });
    }

    private ParallelGatewayPair createParallelGateways(Document doc, String gatewayName, String baseId) {
        // 创建分叉网关（Diverging）
        Element forkGateway = doc.createElement("bpmn:parallelGateway");
        forkGateway.setAttribute("id", "Fork" + baseId);
        forkGateway.setAttribute("name", gatewayName + "并行开始");
        forkGateway.setAttribute("gatewayDirection", "Diverging"); // 关键属性[8](@ref)

        // 创建合并网关（Converging）
        Element joinGateway = doc.createElement("bpmn:parallelGateway");
        joinGateway.setAttribute("id", "Join" + baseId);
        joinGateway.setAttribute("name", gatewayName + "并行结束");
        joinGateway.setAttribute("gatewayDirection", "Converging"); // 关键属性[8](@ref)

        // 添加扩展属性（用于流程监控）
//        Element extension = doc.createElement("flowable:customGateway");
//        extension.setAttribute("originStep", gatewayName);
//        forkGateway.appendChild(extension);

        return new ParallelGatewayPair(forkGateway, joinGateway);
    }

    private void handleEdgeConnections(Document doc, Element process,
                                       X6Edge edge, Map<String, ParallelGatewayPair> gatewayMap) {
        // 增加空值保护
        if (edge.getSourceId() == null || edge.getTargetId() == null) {
            throw new RuntimeException("边缺少source/target属性: " + edge.getId());
        }

        Element sequenceFlow = doc.createElement("bpmn:sequenceFlow");
        sequenceFlow.setAttribute("id", edge.getId());

        // 源处理：映射到网关或节点
        String sourceRef = gatewayMap.containsKey(edge.getSourceId()) ?
                gatewayMap.get(edge.getSourceId()).getForkGateway().getAttribute("id") :
                edge.getSourceId().replaceAll("-", "");

        // 目标处理：同上逻辑
        String targetRef = gatewayMap.containsKey(edge.getTargetId()) ?
                gatewayMap.get(edge.getTargetId()).getJoinGateway().getAttribute("id") :
                edge.getTargetId().replaceAll("-", "");

        sequenceFlow.setAttribute("sourceRef", sourceRef);
        sequenceFlow.setAttribute("targetRef", targetRef);

        process.appendChild(sequenceFlow);
    }

    public Map<String, X6Edge> parseEdgesFromX6(JSONObject x6Data) {
        Map<String, X6Edge> edgeMap = new LinkedHashMap<>();

        // 1. 从X6原始数据中提取边集合（参考网页6、网页7的X6数据结构）
        JSONArray edges = x6Data.getJSONArray("cells");
        if (edges == null || edges.isEmpty()) return edgeMap;

        // 2. 遍历边数据构建对象（网页6的DAG图边结构）
        edges.forEach(edgeJson -> {
            JSONObject edgeObj = (JSONObject) edgeJson;
            X6Edge edge = new X6Edge();
            if ("edge".equals(edgeObj.getString("shape"))) {
                // 基础字段解析（网页6的DAG边属性）
                edge.setId("_" + edgeObj.getString("id").replaceAll("-", ""));
                edge.setSourceId(extractNodeId(edgeObj.getJSONObject("source"))); // 处理source.cell格式
                edge.setTargetId(extractNodeId(edgeObj.getJSONObject("target")));

                edgeMap.put(edge.getId(), edge);
            }



            // 条件表达式提取（参考网页2的langgraph条件分支逻辑）
//            JSONObject attrs = edgeObj.getJSONObject("attrs");
//            if (attrs != null) {
//                JSONObject labelAttrs = attrs.getJSONObject("label");
//                if (labelAttrs != null) {
//                    edge.setConditionExpression(labelAttrs.getString("text", null));
//                }
//            }

            // 边有效性校验（防止生成非法BPMN）
//            if (isValidEdge(edge)) {
//                // ID规范化处理（符合BPMN ID规则）
//                edge.setId(edge.getId().replaceAll("^[^a-zA-Z]", "E_"));
//                edgeMap.put(edge.getId(), edge);
//            }
        });
        return edgeMap;
    }

    // 辅助方法：提取嵌套的节点ID（处理X6的source/target对象结构）
    private String extractNodeId(JSONObject nodeRef) {
        if (nodeRef != null && nodeRef.containsKey("cell")) {
            return nodeRef.getString("cell");
        }
        return nodeRef != null ? nodeRef.getString("id") : null;
    }

    // 边有效性校验（参考网页3的FPGA时序约束校验逻辑）
    private boolean isValidEdge(X6Edge edge) {
        return edge.getSourceId() != null
                && edge.getTargetId() != null
                && !edge.getSourceId().equals(edge.getTargetId()); // 防止自环边
    }

}
